{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep Convolutional Generative Adversarial Network Example\n",
    "\n",
    "Build a deep convolutional generative adversarial network (DCGAN) to generate digit images from a noise distribution with TensorFlow v2.\n",
    "\n",
    "- Author: Aymeric Damien\n",
    "- Project: https://github.com/aymericdamien/TensorFlow-Examples/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DCGAN Overview\n",
    "\n",
    "<img src=\"https://camo.githubusercontent.com/45e147fc9dfcf6a8e5df2c9b985078258b9974e3/68747470733a2f2f63646e2d696d616765732d312e6d656469756d2e636f6d2f6d61782f313030302f312a33394e6e6e695f6e685044614c7539416e544c6f57772e706e67\" alt=\"dcgan\" style=\"width: 1000px;\"/>\n",
    "\n",
    "References:\n",
    "- [Unsupervised representation learning with deep convolutional generative adversarial networks](https://arxiv.org/pdf/1511.06434). A Radford, L Metz, S Chintala, 2016.\n",
    "- [Understanding the difficulty of training deep feedforward neural networks](http://proceedings.mlr.press/v9/glorot10a.html). X Glorot, Y Bengio. Aistats 9, 249-256\n",
    "- [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](https://arxiv.org/abs/1502.03167). Sergey Ioffe, Christian Szegedy. 2015.\n",
    "\n",
    "## MNIST Dataset Overview\n",
    "\n",
    "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n",
    "\n",
    "In this example, each image will be converted to float32 and normalized from [0, 255] to [0, 1].\n",
    "\n",
    "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n",
    "\n",
    "More info: http://yann.lecun.com/exdb/mnist/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras import Model, layers\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# MNIST Dataset parameters.\n",
    "num_features = 784 # data features (img shape: 28*28).\n",
    "\n",
    "# Training parameters.\n",
    "lr_generator = 0.0002\n",
    "lr_discriminator = 0.0002\n",
    "training_steps = 20000\n",
    "batch_size = 128\n",
    "display_step = 500\n",
    "\n",
    "# Network parameters.\n",
    "noise_dim = 100 # Noise data points."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prepare MNIST data.\n",
    "from tensorflow.keras.datasets import mnist\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n",
    "# Convert to float32.\n",
    "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n",
    "# Normalize images value from [0, 255] to [0, 1].\n",
    "x_train, x_test = x_train / 255., x_test / 255."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Use tf.data API to shuffle and batch data.\n",
    "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
    "train_data = train_data.repeat().shuffle(10000).batch(batch_size).prefetch(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create TF Model.\n",
    "class Generator(Model):\n",
    "    # Set layers.\n",
    "    def __init__(self):\n",
    "        super(Generator, self).__init__()\n",
    "        self.fc1 = layers.Dense(7 * 7 * 128)\n",
    "        self.bn1 = layers.BatchNormalization()\n",
    "        self.conv2tr1 = layers.Conv2DTranspose(64, 5, strides=2, padding='SAME')\n",
    "        self.bn2 = layers.BatchNormalization()\n",
    "        self.conv2tr2 = layers.Conv2DTranspose(1, 5, strides=2, padding='SAME')\n",
    "\n",
    "    # Set forward pass.\n",
    "    def call(self, x, is_training=False):\n",
    "        x = self.fc1(x)\n",
    "        x = self.bn1(x, training=is_training)\n",
    "        x = tf.nn.leaky_relu(x)\n",
    "        # Reshape to a 4-D array of images: (batch, height, width, channels)\n",
    "        # New shape: (batch, 7, 7, 128)\n",
    "        x = tf.reshape(x, shape=[-1, 7, 7, 128])\n",
    "        # Deconvolution, image shape: (batch, 14, 14, 64)\n",
    "        x = self.conv2tr1(x)\n",
    "        x = self.bn2(x, training=is_training)\n",
    "        x = tf.nn.leaky_relu(x)\n",
    "        # Deconvolution, image shape: (batch, 28, 28, 1)\n",
    "        x = self.conv2tr2(x)\n",
    "        x = tf.nn.tanh(x)\n",
    "        return x\n",
    "\n",
    "# Generator Network\n",
    "# Input: Noise, Output: Image\n",
    "# Note that batch normalization has different behavior at training and inference time,\n",
    "# we then use a placeholder to indicates the layer if we are training or not.\n",
    "class Discriminator(Model):\n",
    "    # Set layers.\n",
    "    def __init__(self):\n",
    "        super(Discriminator, self).__init__()\n",
    "        self.conv1 = layers.Conv2D(64, 5, strides=2, padding='SAME')\n",
    "        self.bn1 = layers.BatchNormalization()\n",
    "        self.conv2 = layers.Conv2D(128, 5, strides=2, padding='SAME')\n",
    "        self.bn2 = layers.BatchNormalization()\n",
    "        self.flatten = layers.Flatten()\n",
    "        self.fc1 = layers.Dense(1024)\n",
    "        self.bn3 = layers.BatchNormalization()\n",
    "        self.fc2 = layers.Dense(2)\n",
    "\n",
    "    # Set forward pass.\n",
    "    def call(self, x, is_training=False):\n",
    "        x = tf.reshape(x, [-1, 28, 28, 1])\n",
    "        x = self.conv1(x)\n",
    "        x = self.bn1(x, training=is_training)\n",
    "        x = tf.nn.leaky_relu(x)\n",
    "        x = self.conv2(x)\n",
    "        x = self.bn2(x, training=is_training)\n",
    "        x = tf.nn.leaky_relu(x)\n",
    "        x = self.flatten(x)\n",
    "        x = self.fc1(x)\n",
    "        x = self.bn3(x, training=is_training)\n",
    "        x = tf.nn.leaky_relu(x)\n",
    "        return self.fc2(x)\n",
    "\n",
    "# Build neural network model.\n",
    "generator = Generator()\n",
    "discriminator = Discriminator()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Losses.\n",
    "def generator_loss(reconstructed_image):\n",
    "    gen_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n",
    "        logits=reconstructed_image, labels=tf.ones([batch_size], dtype=tf.int32)))\n",
    "    return gen_loss\n",
    "\n",
    "def discriminator_loss(disc_fake, disc_real):\n",
    "    disc_loss_real = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n",
    "        logits=disc_real, labels=tf.ones([batch_size], dtype=tf.int32)))\n",
    "    disc_loss_fake = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n",
    "        logits=disc_fake, labels=tf.zeros([batch_size], dtype=tf.int32)))\n",
    "    return disc_loss_real + disc_loss_fake\n",
    "\n",
    "# Optimizers.\n",
    "optimizer_gen = tf.optimizers.Adam(learning_rate=lr_generator)#, beta_1=0.5, beta_2=0.999)\n",
    "optimizer_disc = tf.optimizers.Adam(learning_rate=lr_discriminator)#, beta_1=0.5, beta_2=0.999)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optimization process. Inputs: real image and noise.\n",
    "def run_optimization(real_images):\n",
    "    \n",
    "    # Rescale to [-1, 1], the input range of the discriminator\n",
    "    real_images = real_images * 2. - 1.\n",
    "\n",
    "    # Generate noise.\n",
    "    noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n",
    "    \n",
    "    with tf.GradientTape() as g:\n",
    "            \n",
    "        fake_images = generator(noise, is_training=True)\n",
    "        disc_fake = discriminator(fake_images, is_training=True)\n",
    "        disc_real = discriminator(real_images, is_training=True)\n",
    "\n",
    "        disc_loss = discriminator_loss(disc_fake, disc_real)\n",
    "            \n",
    "    # Training Variables for each optimizer\n",
    "    gradients_disc = g.gradient(disc_loss,  discriminator.trainable_variables)\n",
    "    optimizer_disc.apply_gradients(zip(gradients_disc,  discriminator.trainable_variables))\n",
    "    \n",
    "    # Generate noise.\n",
    "    noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n",
    "    \n",
    "    with tf.GradientTape() as g:\n",
    "            \n",
    "        fake_images = generator(noise, is_training=True)\n",
    "        disc_fake = discriminator(fake_images, is_training=True)\n",
    "\n",
    "        gen_loss = generator_loss(disc_fake)\n",
    "            \n",
    "    gradients_gen = g.gradient(gen_loss, generator.trainable_variables)\n",
    "    optimizer_gen.apply_gradients(zip(gradients_gen, generator.trainable_variables))\n",
    "    \n",
    "    return gen_loss, disc_loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "initial: gen_loss: 0.694535, disc_loss: 1.403878\n",
      "step: 500, gen_loss: 2.154825, disc_loss: 0.429040\n",
      "step: 1000, gen_loss: 2.103464, disc_loss: 0.502638\n",
      "step: 1500, gen_loss: 2.282369, disc_loss: 0.572065\n",
      "step: 2000, gen_loss: 2.711531, disc_loss: 0.341791\n",
      "step: 2500, gen_loss: 2.835576, disc_loss: 0.322356\n",
      "step: 3000, gen_loss: 2.970127, disc_loss: 0.275483\n",
      "step: 3500, gen_loss: 2.836321, disc_loss: 0.190680\n",
      "step: 4000, gen_loss: 2.892855, disc_loss: 0.337681\n",
      "step: 4500, gen_loss: 2.948962, disc_loss: 0.329322\n",
      "step: 5000, gen_loss: 3.799061, disc_loss: 0.327288\n",
      "step: 5500, gen_loss: 4.090328, disc_loss: 0.274685\n",
      "step: 6000, gen_loss: 4.343777, disc_loss: 0.155223\n",
      "step: 6500, gen_loss: 3.806556, disc_loss: 0.155855\n",
      "step: 7000, gen_loss: 4.827947, disc_loss: 0.078372\n",
      "step: 7500, gen_loss: 3.708949, disc_loss: 0.140979\n",
      "step: 8000, gen_loss: 5.250406, disc_loss: 0.164736\n",
      "step: 8500, gen_loss: 5.491106, disc_loss: 0.110080\n",
      "step: 9000, gen_loss: 4.391072, disc_loss: 0.100240\n",
      "step: 9500, gen_loss: 5.074200, disc_loss: 0.105567\n",
      "step: 10000, gen_loss: 6.077592, disc_loss: 0.215981\n",
      "step: 10500, gen_loss: 4.468120, disc_loss: 0.099412\n",
      "step: 11000, gen_loss: 5.887744, disc_loss: 0.093534\n",
      "step: 11500, gen_loss: 5.656942, disc_loss: 0.075079\n",
      "step: 12000, gen_loss: 4.752551, disc_loss: 0.092505\n",
      "step: 12500, gen_loss: 5.682284, disc_loss: 0.077406\n",
      "step: 13000, gen_loss: 5.386811, disc_loss: 0.101259\n",
      "step: 13500, gen_loss: 4.646158, disc_loss: 0.039680\n",
      "step: 14000, gen_loss: 5.698145, disc_loss: 0.083461\n",
      "step: 14500, gen_loss: 4.513465, disc_loss: 0.077946\n",
      "step: 15000, gen_loss: 5.586041, disc_loss: 0.039668\n",
      "step: 15500, gen_loss: 6.316034, disc_loss: 0.084526\n",
      "step: 16000, gen_loss: 7.120735, disc_loss: 0.070267\n",
      "step: 16500, gen_loss: 5.511638, disc_loss: 0.053082\n",
      "step: 17000, gen_loss: 8.176781, disc_loss: 0.029212\n",
      "step: 17500, gen_loss: 6.144678, disc_loss: 0.140460\n",
      "step: 18000, gen_loss: 5.583275, disc_loss: 0.077311\n",
      "step: 18500, gen_loss: 5.840647, disc_loss: 0.042103\n",
      "step: 19000, gen_loss: 7.111396, disc_loss: 0.030435\n",
      "step: 19500, gen_loss: 4.391251, disc_loss: 0.043656\n",
      "step: 20000, gen_loss: 6.271616, disc_loss: 0.040568\n"
     ]
    }
   ],
   "source": [
    "# Run training for the given number of steps.\n",
    "for step, (batch_x, _) in enumerate(train_data.take(training_steps + 1)):\n",
    "    \n",
    "    if step == 0:\n",
    "        # Generate noise.\n",
    "        noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n",
    "        gen_loss = generator_loss(discriminator(generator(noise)))\n",
    "        disc_loss = discriminator_loss(discriminator(batch_x), discriminator(generator(noise)))\n",
    "        print(\"initial: gen_loss: %f, disc_loss: %f\" % (gen_loss, disc_loss))\n",
    "        continue\n",
    "    \n",
    "    # Run the optimization.\n",
    "    gen_loss, disc_loss = run_optimization(batch_x)\n",
    "    \n",
    "    if step % display_step == 0:\n",
    "        print(\"step: %i, gen_loss: %f, disc_loss: %f\" % (step, gen_loss, disc_loss))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Visualize predictions.\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAFpCAYAAACBNaNRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzsnXecXFX5/99HQCmi9A4SBUE6cUEQpSO9g/RepUgVgggICCJ8RVB+lAgICNKl14A0pUikd5AaWqRIFwi5vz8m7znJzW52s7szc2fzvF+vvCYzOzt77pxz7/08z3lKKoqCIAiCoH35UqsHEARBEPSNuJAHQRC0OXEhD4IgaHPiQh4EQdDmxIU8CIKgzYkLeRAEQZsTF/IgCII2p2EX8pTSGimlp1NKz6WUhjTq7wRBEEzqpEYkBKWUJgOeAVYDRgD3A1sURfFEv/+xIAiCSZxGKfKlgeeKoni+KIrPgIuA9Rv0t4IgCCZpJm/Q584JvDLW8xHA97p680wzzVTMO++8DRpK69DaSSm1eCRBELQbL774Im+99VaPLh6NupB39sfH8eGklHYFdgWYZ555GD58eIOGEgRB0H50dHT0+L2Ncq2MAOYe6/lcwGtjv6EoiqFFUXQURdEx88wzN2gYQRAEA59GKfL7gflTSoOAV4HNgS0b9Leawv/+9z8ARo0aBcBXv/rVVg4nCFpK2W3o+TH55JOP8xg0h4Z820VRjEop7QXcBEwGnF0UxeON+FtBEASTOg27bRZFcT1wfaM+v1k89dRTAGy44YYALLDAAgAMHToUgFlmmaXhY1D9PPLIIwAstthiQGyiBs3Fdfj222+z1157AfDvf/8bgIceegiA6aabDoB1110XgOOOOw5oznkyKROZnUEQBG1OQxKCJpaOjo6iKlErZfW72mqrAfDWW28BMMUUUwAw33zzAXDhhRcCWSU3AlXP0UcfDcCee+4JwFJLLdWwv9lKRo8eDdSUH8Cjjz4KwE033QTAnXfeCcAbb7wBwJe+VNMjc89d218fMqSWSLz66qsD7WG5fPLJJ0C2+NyLefXVV1s+fsey1lprAfD3v/+9/prni3M22WSTjfP8y1/+MgDPPPMMAHPNNVeTRt17PKb33nsPgIMOOgiASy+9FIBvfvObQL4G+HPnrr/2zzo6Ohg+fHiPJj8UeRAEQZszyW8tqyyefPJJAE4++WQAbr31VgD++9//AjDNNNMAsOqqqwJw0kknAbUY+Ebx8ssvA7DyyisD2SqYddZZgYGnyL/44gsA/vjHPwJw5JFHAvm4u7IeywroyiuvBHIc7kwzzdT/g+0nPKbddtsNgE8//RSAU089FaiGNbH//vsDcMcddwA11e13Pv/88wPw3e9+F8jnxxFHHAHULAqAa6+9FsjHWYXjGjFiBABrrrkmAM8//zyQI3DK683nDzzwAJCt9quuugoAkxrvv/9+pp122gaOfHxCkQdBELQ5k5wi1xd53333AXDdddcBcNFFFwFZEXlHXW+99QC44IILAPjKV77StLFuv/32QFY1Jk6pkAYaKvFDDz0UgA8++GCcn0811VTjPOqTdN/in//8JwBnn302AA8//DAA99xzTyOH3SdUfx7ToosuCmSFWwW0bF55pVZ14+ijj67vR3z961/v9HeMWnE/R7VbBSWugt5hhx0A+PDDDzt9n1bHlFNOCeTY+I8++gjIc6cl+cILLwA1q+Mvf/lLI4beJaHIgyAI2pxJRpHfeOONAPzsZz8D4PXXXwfy7rpsvvnmQPaVq/aaiXd6FaZss802QPaRDxTchzjhhBOA7Is09lif98ILLwzkSAjnzt8fPHgwkOfWfY8qc+aZZwI5ImKOOeYAaLqPdUJ4Tmy99dZAjhKaEJ43nkennHIKkPekWpn5WbYKXE9aRSuttBIAv/rVrwAYNGgQkNfbO++8A8B2220HwO233w7AZ599BtSuNVr2zbLgQ5EHQRC0OQNekRu/aiaaflf9frvvvjuQd66b6QPvCn3jKnMVw84779yj3y9bGT1RUK3E2OPPP/8cyDH5l19+OQAzzjjjBH/Pnxs18Nprtfps+i6rjFaIfletixlmmKFlYyrj+usNqtRLLrkEgCWXXBLI0S2tYKGFFgLyua51sMceewDw85//HOj6uGeffXYAzj33XAC+853vAPD+++8DtWPWGlxiiSX6ffydUe0zPAiCIOiWAavIR44cCcC2224LwJtvvgnk7LQ//elPAEw99dQtGF3nqKSNYRcVQ1cRAmWefvppINe5+MMf/gDA1772tX4ZZ39jdIDxuVoQ3R2vvnQV+OOP1+qylbMKq9jgwzE6NtWhkUlVGmtf0Io0guhf//oX0FpFrs/7mGOOAWDttdcGJn7vyWgW16mK/JNPPuHwww8HcoRMo+dzwF3IP/74YwC23LJWNfeJJ2ptQldZZRUgh7hV6QIuppxrZjv5nuQ9XQyPPfYYAOeffz6QQ7/uuuuu/htsP+JxeaOxUJnfw7333gvk5BPf7w3Kk0W3mT/XhK7iRVGh4dgMgXv22WdbNqZG4Hnm5qchet7IWuH20yW344479uj93mx1/emy+8c//gHkUhIyevToehkJN0a7cg/2F+FaCYIgaHMGlCIfPXp0Pa37/vvvB/Ld18I2VQrrKqNyVsWMbapBtja6w80YS4qq/v773//WX6sif/7znwE46qijgKx0PP5yyrRzq+tJV4oWjCFvVcQQt7Iy32WXXVo2pv7EQm+idWSa/4knnghk12eVytxqtVuG46c//SmQFbmhzH/729+AHJQgk002WV2BW/BtxRVXbOiYQ5EHQRC0OQNKkd98882cc845QPa9GZJmooWqrqzuqhCiZxEffcMmT/h4yCGHALl0bleohgz9MkHmrrvuqqdOVwk3K7WatEQ87u6wrKgp+25oL7744v06zv7AOXnuueeA7G91PVZxzD1BS/g3v/kNkK0lwyjd/5Ff/OIXQC6u5Yb33//+dyDvhzQTww4NEhAVuRafytxH8Rryne98p/49LL300o0b8Nh/uyl/JQiCIGgYA0KR6zvedttt62pu+umnB7Lv7fe//z2Q76Le+VV9Bx54YP0zoDWRDv5Nfb5l39sVV1wB5AiHcvlWfeymFnusqr0LL7yQFVZYAahWKKLJIqq48nfvcxVPOXRPi8P9D1PKq4hRGx6L689jNw2+ShhJ8+6779Zfu+WWW4CswE1J1+IwSuU///kPkC0P57IcCeI5bFiiiXytSNDzvPI885h8dA/LsXkMzukmm2zC8ssvP857G00o8iAIgjZnQChyy5a+++679buibZhsSmACUDkWVCwNazsxIyhUSs3ASIa9994byDv7qh0ff/nLXwK5vIDH6PMXX3wRGN//eskll9RV+9133w20fm/gww8/ZMEFFwTgW9/6FpD9x7PNNhuQfZc/+tGPgBwJYBlSFbk5A31JKW8UWhGW6C1/7yq3Zq637nBdufcgY6tk15ZrTSVuYpPPLcvr47Bhw4BcGE7r07l0P8hzoBkce+yxQE6rN868HC2mVeFcOXfuB6y99tpNz1MJRR4EQdDmDAhFrmIYuwXVcsstB+TGESoiC+IbHaB6Na396quvBuDXv/41kHfXm4H+fH3cKgPL12pNWBrUoj0qAfcHumqJNnr06Lrfr5WZdWMz+eST873vfQ/I5UC784sakzt06FCg5pOEXIa0Cg3Fy5jX4HorRzyoYPXDVqF4m2NVeaq6P/300/o+jueT68n3OBfXXHMNMH5W7gEHHADAxRdfDMB+++0H5ExIz9tmKnIx6mmZZZYBsvXq3Li+fG7ehnNo1FkzCUUeBEHQ5gwIRW7m39xzz82mm24KUH80ftWCOCoAC9289957ABx22GFAjgzRp37wwQcDzW0woUq2tO6uu+4KwGmnnQZk1WMUgcV7VE6OtRyHXhRFvaGzWZOtblIx5ZRT1sc/sWhFlXMDWm1ldIYZtdbBUcE6ZzvttBNQDSUuNhZ37NaymWGGGeqRQZaHVoGbE+AcfPvb3+70s93HWH/99YFcwMo1Peecc/bjkUwcriPHqPWhAi9Hk7mn4/czyyyzND3qrXorPgiCIJgoBoQiX2ONNYCab6vcQspMz66wJoKREVYItNFsK9Hfv9tuuwFw/fXXAzkDVFQ/Kihj6G1SMHZbO3fgjQow4qedMHrnr3/9K5D9zTYu7ov15PfT31EH5c/VSvJ5FWusuPei9epYX3jhhS6tHv3FPcU17fmmknUuW4Gx7JZV1mLUWlKZaxlrXfn6xx9/HIo8CIIgmDjaWpF7J1SB9eYuqD9M37l+V1VtK/2tHs8iiywC5MgH/ZO33XYbkBW3WKdbReHO/wcffFD/zozf7a+mC6pkM/n0deoj7Q/fr2M3wsEmBc6/Cr0v9NZf3x1mR7qfI66zKlbl1LrV0jOKqifnhHNV3gtwnZlpbUZ1Oa/DSKZmct999wH5PPPc0Jpy3Vmx8qKLLgLyMZr3YOOKZtLrq1RKae6U0m0ppSdTSo+nlPYZ8/oMKaVhKaVnxzxO33/DDYIgCMr0RZGPAg4oiuKBlNK0wL9SSsOA7YFbi6I4LqU0BBgCHNz3oWa8U1rlz13kQYMGjVd/pKvfNdZT36TxrioHlUKVMu30WV577bVAzl599dVXgaxqBg8eDGQlf8YZZwC1Y1a92jC2v3x5fq+XXXYZkC2bH//4xwD1Gi+9sXDKGa1GFulntoFxf0TgNMq3aUxyudaIx1DFLkayzz77AHDqqacCtfOtK8vFaKibb74ZyHNlDSNrs2gtqma12MwJMTegmRjbXsaM4qOPPhrIHatU5OK51YruY71W5EVRvF4UxQNj/v8B8CQwJ7A+cO6Yt50LbNDXQQZBEARd0y8+8pTSvMCSwH3ArEVRvA61i31Kqd9bf3gXt4Kh6vmzzz6rZ2wan6oP0hhPm8CqYh966CEg+wNVAl3dnauA8a32DNQnZ5SK9WLKu+tf+tKX6vG+fj/9hQrNCButAqMS9D/qR5wQRqHY9/C8884D8vF6XOYK+DerjFm7ZYvEKI8q1oeR1VdfHaDeUHjQoEH17OMnn3wSyPP70ksvAdni9VGL17nzfNMydA633357oLnfh9cTrwUqa61JOwY5ZiPcXKflqqU9raPfn/R5Jy+l9FXgcmDfoijen4jf2zWlNDylNNwNsiAIgmDi6ZMiTylNQe0ifkFRFIYMvJlSmn2MGp8dGNnZ7xZFMRQYCtDR0TFRxTHK3UesHzJq1Kjxdp5VQN41fTR+fKmllgKy/7VZHT36A+tcWKXO2ivWvDYyxUiJL774om6ZNKp3p6rEDD93/DfeeGMg+1m1Ir744ot6zRQ7H2ktuBeg9eSYPc61114b6F//cqN81aq0aaaZBshr2MzhKvvI9V9bY/vCCy+sK3IjOFS14vF53K4LrShVbhWida688kog77XNNNNMAGy55ZZAPn/sYOV5Jc6dlotWbzPpS9RKAs4CniyKYuzKNlcD2435/3bAVb0fXhAEQdAdqbeV4lJKPwDuAh4FvB3/nJqf/BJgHuBlYNOiKN6Z0Gd1dHQUw4cP79U4IEevbLDBBuOo87Efl1xySSBXNtP/ZcWyKiui3uLcWlt9p512qvuyVbn93SlIH6jftxX0ROWmavnoo4/qPnxVm35jraXNNtsMyHPXSMXTqKqQrkstES2jH/zgB0DuuFNltHL33Xffuu/bHAbVrNFJKm6t5mbWKppYtC7sO+q1oJzJ6WMZ9+GsY9Rd5FxP6ejoYPjw4T26MPXatVIUxd+Brv7IKr393CAIgmDi6LUi70/6qsiDCeMcL7TQQnR0dABZpTcKMzvtuqL/W/Wtr/idd96px3+b7acvtkqVAPvKm2++CcBiiy0G5LrbxlvbHarKjJ2DYYSUFq1RJu1o2Xosc8wxB5DzFsqUq2xqZRhPvtFGG/XruCZGkUetlSAIgjYnFPkkRn/VVplY9D3rS9VXX8Xa4Y3k9NNPB3J/Ua2QoPW4n6bP3M5ArlVzUNwfuOeee4Dc/ai/CUUeBEEwCRGKPAiCoIKEIg+CIJiEiAt5EARBmxMX8iAIgjYnLuRBEARtTmUu5OWiOwOBzz//vF7qcqAxevToATlnA5miKKhCcEPQ/1TmQh4EQRD0jso0Xx6IiSEWgRqINGK+bBTyyiuvALlY1kBcG62gHdPng54RZ0gQBEGbM3AlYwUIBdQzTNtfccUVgVxGYOGFFwZyoalJHQuOua7CUgkkVkIQBEGbE4o8aDlXXHEFkJW4SnPBBRds2Zhaja3ydt11Vx588EEgNwE55ZRTAFhllSj7XyVsPNGKRtqhyIMgCNqcUORBy7GUq77frbbaCmiNsmk1Nin4yU9+AtRawhn9ZPnUAw88EMhNgKvcRm0gowW50047AXDHHXcA8MADD/D1r3+9qWMJRR4EQdDmTHKK3AgJW5GpgLybfvLJJwDsvffeAKyzzjrNHmKvaVXTiL7wv//9j0cffRTIBfxPPfXUVg6pqdiU2ebLJ598MpDncoEFFqg3bLbhwRJLLNHsYfaYkSNHAnD11VcDuTG6jZs9n5ZZZhkgN6PW/+8aqPIaNlv7pptuAuCGG24AYP311wdouhqHUORBEARtzySjyJ977jkANt98cwAeeeQRgC5rodx6660AbL/99gCceeaZDR7hxKNqe+yxxwC4/fbbAdhzzz2B9ogzfuqpp+rqa6mllgJg6qmnbuWQmoJKfJtttgHyHE422WQA7LHHHkDNVz7XXHMB1Vapzz//PJDHfdtttwH5/HItavl6nFNNNRUA8803H5AjcpZeemmgmsd88cUXA7ltnzWHXL+toPpnehAEQTBBBrwi1ye+1157ATk+txyzXH6uElDlmlWnkmgFqhujGFR1qqD77rsPyErh8ccfb/YQJ5pjjz22/p3ut99+LR5N43GdOWc33ngjADPPPDMAxxxzDABbbrkl0Nr1NiHefPNNIPu8X3/9dQDeeOONcd7neVQ+Dq0uFbmNj1999dUGjbjveP4ddNBB4zyfaaaZAFhuueVaMzBCkQdBELQ9A1aRqww23XRTgHpkhMpaVTvbbLMBuZ7HAw88MM7vv/POO0BW8nPPPXfDx94V5XhhrQ3jiVUIzzzzDJD9rossskizhthj/vOf/wC1vQjnYo011mjlkJrCW2+9BcDDDz8MZGV66aWXAvC9732vNQObCM4//3z22WcfAP773/8C2ZJ1LvUbl883z59pp50WyGvUaJcDDjgAgA022AColo9ci/ftt98G8jXj2GOPBWCeeeZpzcAIRR4EQdD2DDhF/vHHHwNZiVunQrVqvOrWW28NwC9+8QsAvvKVrwBw0kknAdlX6e9VQZGX0a8677zzAlmJq4Z++tOfAnDdddcB2R9ZBdx7GD16NIsvvjgwsDMU9Y2fddZZADz55JMAzD///EB7KPEPPvgAqMWzv//++0A+LpW3yty1pjVoFIvZuu+99x5Qi5OH7HPXYrFuiXHlVeCoo44C8jEYHWZMfCvPr1DkQRAEbc6AUuSjRo3ikEMOAeChhx4CsjpVceuHPfroowGYZpppgOxvNkbU31PBV7km9lVXXQXA8ssvD+SsVSMJVDdVUORaTKeddhpQ84EeeeSRrRxSU1CBmrmpz9gM4iqj6j744IOBmnVaju5aaKGFgJyVq5Jecsklx3mfmP1oRqsRH59++imQo3n0lVcB9888lsMOOwzIVvsuu+zSmoHRD4o8pTRZSunBlNK1Y54PSindl1J6NqV0cUpp0qt8FARB0ET6Q5HvAzwJfG3M898AvyuK4qKU0unATsBp/fB3uuUvf/kL5513HpBVhHd6fd7f/e53gay4Vave+d1FF++2Va7Ep59xxx13BLLq04+pb7MVNSDKGAX08ssvAzUratlll+2Xz3bOfSxn7WqVtQIzFq1gaOyxc2McdRX7vGrdnn/++UCtHlE57n277bYDJj6bWItYPB+NMquCIvdaoTXpGN0XmGWWWVozsLHokyJPKc0FrA2cOeZ5AlYGLhvzlnOB1s9EEATBAKavt/+TgIOAacc8nxH4b1EUo8Y8HwHM2ce/0S1mOB5zzDF1X/cWW2wB5BopZaXg3dR4XqMoRD/YN77xjU5/v0o4Vn2YRt4Y46v6qwIqcf33s8wyS6+tHVWsmay//e1vgRwBIXPMMQeQVV4z9wpcm1pJrk/3Xqz5Y0SEas/vae+99+43i6W3/PrXvwayIk0p1X3iq622GpDnwgzOnmakWiXRz5ZWH/PYeK3Q0lOhO5f77rsvkKsftoJeX51SSusAI4ui+NfYL3fy1qKL3981pTQ8pTTc5JAgCIJg4umLIl8OWC+ltBYwJTUf+UnAdCmlyceo8rmA1zr75aIohgJDATo6Ojq92PeUoUOHAvDSSy/VY5HtOtOVkjY764gjjgDyXVdUTCr6KmWYdYXHrG9YVdRK33AZMxidl913332iP0MltNtuuwHw97//fZyfGzGhv9mIEZW6cffNwKw/x+D6fOGFF8Z5VImXufHGG/nTn/4ENF/xqUCfffbZcZ5PPvnkzDjjjEDel3niiSeAbIFo9TgXJ5xwAgAbbbQRkOfw7LPPBsaPLqtSv1bPI/cFzCnxmuDc+thW9ciLojikKIq5iqKYF9gc+FtRFFsBtwGbjHnbdsBVfR5lEARB0CWN2CI/GLgopfQr4EHgrAb8jXGw1vg000xT9xOXd8NFX97Pf/5zIGecqci9y9qNxazDduDKK68c57nfQZWsCaM2VGArrbRSj39XZWitaudM1WtFQV83rt7s3nvuuQdojiJ3nf35z38G8hz4usw+++xA9kPrx7/mmmuAWocdI0K0KJplYTlmI70cW1EU9bojjkmLwt8xGkcVbzVHrYq77roLyFFMvk81675GFdB6tDenMe6LLrookCPa7GGw4YYbNv2c65cLeVEUtwO3j/n/88DS/fG5QRAEQfdUL2h1IvAu/tRTTwE1X5aK78UXXwRyxMYrr7wC5GgWa1145/SuazW6zTbbrNHD73fs0qJPz25IVWLnnXcG8lh7Un9adWd2rfN+4oknAlmJi3PpPocVLidG/fcVFbUb+eWoDmv96CMW16d1chZaaKH68bumzRtoFmYsGkc+atSoetSRx+V5pLVg7RiP17FbD0h/cvn3Z5111nGeVwn30w4//HAg70UdeOCBAPzud78Daoq82VQ3pi4IgiDoEQNCkU833XRA7e5vDQ9ji1Xo5Sw/d9W98/tZhx56KAAzzDBDI4fer3hsPpZ306uE/lZV8rHHHssPfvADIEcFlON1b775ZiBHfKhWy0pc/H1Vv/0gXSfNQNVWjkYZPHgw0H0PWJXt5JNPXp9PM3WbjTHjRqB89NFH43XUsq+ovm+fi2tTC2WVVVYBskL3GL/97W8Dee6r2CHJsXptMZtcn3krrIlQ5EEQBG1OWyty1cBPfvIToOb3trOPkQsqAe/wxr+q/l566aVxPnOttdYCqqkEusJolbLV0VVscispd4lfY4016pFBc85ZSwJed911AepdaIxs0OetIi/jnBsBom+5XM+kGbgOVa6iT7yr/AbXqf7/Dz74oK7OF1544YaMtTuMfrr88suBWgSKWcNf/epXAbj77ruBHIVTRmvKn6+wwgpALSpnbIxM+uSTT8b5/Crxhz/8AYAhQ4YAeS6tQtoKQpEHQRC0OW2tyEV/20orrVTvhvOtb30LyH6re++9F8gxn/pPrfGg2mtm1l9fUe2Z2aiaUyGo6qqIXVU22GCDupL5179q1R7MC7BD+5133glkdWaMv5EfWiTGbKtg9dP6/maqu7JF55x85zvfmeDvqUjNgIWc5diqDF3Hblz+EksswR133AHkjlldKfEy+o/1t/vZfl/GkZety1aghafV4P6bNdc9//74xz8Crd1XGxAXchfDSSedVD9RTN/2wu2X7oaRFxITGkxA6CqRqIpYZsDNQI/Vm1hPT65W4JydccYZ9Q0yXSCmeRsuaDq3rhEv9J5QfpaPFnLaf//9x/mcZuBYyxeiQYMGAdndVb4o6+Iz5d3wvtlmm60e1tZqd58lD8YucmaZ6LKI6ArPQ5uflMN/vRhWocXb6quvDmS3UbmNnUJim222acHoxiVcK0EQBG3OgFDk8uUvf7mezlxu5FtOWCgrppVXXnmc91UZVc31118PjJ/2vvbaa7dmYL1gqqmmqheOshG2LqHuwu3KZrob1TY7sLFxM/nHP/4BZOtB9arLzoa9jt2yAWM32oCs+jbbbLN6yGarcX19+OGHdXWu28e50jXS1Xmku8LNTN/no1ZkK60P281ZYrh8LdFdayGwKpS4bv0IgiAIgj4xoBQ5ZH+xjVJVeSqdM844A8g+SH3i+rvaCUPcDM9TxRx00EEtG1NfcFPSJgvDhg0DcsElrSj3ANxkUhVWIVRNZaqVUE5RHz58ODB+I4Vpp631ZrGs73rrrQfAUkstVZkyxB7LnnvuWW8abQlpx+/+RDkhSNwwvPbaa4E8p56HtnZrZWtFrQ3DYg2U8JhuuukmoBrNzCUUeRAEQZsz4BT5kUceCeSQIYsX6Vc24kFfndEqSyyxRFPH2RfKvmGPzYQRk57aFX2ORg0YndMOWKZ1nnnmAeCcc84Bcgil686EKIuzmQZf9sdWkR/96Ed11WqK/WGHHQZk//JWW20FZKVtMwa/h/JxHnLIIQCsuuqqjRx6j3D92XRaf77RTxbWqxKhyIMgCNqcVE4hbgUdHR2FvsO+Yly4sZ3GgBq/W26DZhSBDYvbIWpFbFy77bbbAlntWO41CBpBURT1MgnnnnsukJW3e1Eq9rELbUH2K+sDN6HIvS1/rwq4N6EVVS5L0Gg6OjoYPnx4jy5IociDIAjanOrc/voJC9PrGzet2Oaw3l2981exHVpPMbLB0qDt4F8N2p+UUj3b1LK8FgMzWky/8ti/Azm6xUgkfeNVUuLiMVQhTrw7qj/CIAiCYIIMOB95d3iXHTlyJJAVfBVqOwRBu6OP3OJfRhzNMsssQM4BaHXdmHYgfORBEASTENVzTDUYd82/8Y1vtHgkQTDw0NfdXbneoH8JRR4EQdDmTHKKPAjbxgsHAAAgAElEQVQmVdwPs2a6+0LtGLEVjEso8iAIgjanMoq8KIoBpwxUQAPtuCBnyraySl0wcbgO+1q174svvqhnSDv/rYy17ml3ooHMpHvkQRAEA4TKKPKBqFoH4jFJKPFJl48++qheG8Xet13VH28Gk7ISl/gGgiAI2pzKKPIgCNqDIUOG1Dtt/ehHPwLghhtuaOWQJnn6pMhTStOllC5LKT2VUnoypbRsSmmGlNKwlNKzYx6n76/BBkEQBOPTV0V+MnBjURSbpJS+DEwN/By4tSiK41JKQ4AhwMF9/DvBJMBZZ50FUK91bd0OfaDuOZg1+Oc//xnI3XWCxmKdonPPPbceKXLKKae0ckjBGHqtyFNKXwOWB84CKIris6Io/gusD5w75m3nAhv0dZBBEARB1/RFkX8T+A/wp5TS4sC/gH2AWYuieB2gKIrXU0qz9H2YwUDGPo8HH1wz3FR+XVXmfPDBBwH47ne/C8Cll14KwNprrw0M7GihVvLyyy8Dte/361//OpCrGQatpS8+8smBwcBpRVEsCXxEzY3SI1JKu6aUhqeUhtsYIQiCIJh4+qLIRwAjiqK4b8zzy6hdyN9MKc0+Ro3PDozs7JeLohgKDIVaPfI+jKNH6G+99957ATjuuOMAuP322wHqCuOYY44BYLvttgPaW92VFW0Vj+Wzzz7jqaeeAnLGoT7xcmasNaydS/20e+21FwDzzTcfAAsuuGAzhg7kjlOO6dVXXwXgueeeA+C9994Dctz9UUcdBcCIESOA7O//3ve+B8Cjjz4KwOOPPw7A97//fc455xwg95BsFfrDU0r1/YmBxFtvvQXAVVddBeQa6t/+9reBXNnx3//+N5D3ZuaYYw6gtfHsvf7LRVG8AbySUlpgzEurAE8AVwPbjXltO+CqPo0wCIIgmCB96hCUUloCOBP4MvA8sAO1m8MlwDzAy8CmRVG8M6HPaUaHIO+222+/PZC7dlszQlR98847LwCzzz47ABtsUNuzXWeddYCs/qrQ6eT9998Hcv9Eu33bj3TFFVcE4IADDuCb3/wmAB988AGQeyhOPfXUQPNVxRVXXFFXd++++y4AH3/8MQBrrLEGkL/r1157DchWk53ZHfs999wDwCKLLNLwcXveOOb99tsPgNtuuw3InXG0GlwnHlv5vPPn5d6Vo0aNqq/BF154odP3NJrnn38egAUWqGm2L3/5y/U1V4X131ucm/PPPx+A448/HoA33ngDyNcGlfmbb74J5N64/r4/v+WWWwCYe+65+2V8E9MhqE8roiiKh4COTn60Sl8+NwiCIOg5Az6zU+Wwxx57APDwww8D2bdZxtf1g6mCVF6qva997WsAzDbbbEBz/M8PPfQQAEOHDgVgvfXWA2CLLbYAcp1plYTHfuGFFwJw7bXX1iNC9OmqqFTqRx99NAAbbbRRA48kf8977713XZ1ecMEFQI5GsV62yueVV14B4LDDDhvndd83zzzzNHTMY+OYP/zwQwCefvppIK8TcX1Yi0Tf+Ouvvw7kTlW77747AOuvvz6QrY/NNtusPmdakWuttVZ/H06nGE20yio1Xeb3/dWvfrWtlbg4hw888ACQLTzXpr7vNddcE8jn3wwzzADkOVfBuy7PPPPMpltNUWslCIKgzRlwilzV8Ne//hWAP/7xj0COPValliMiutor8HXvvieffDJQ8zdD9jF/9atf7cejqKGy3nDDDQG48cYbxxmT0Qw+N/LGWuEzzjgjACNH1gKH3nvvvfr3Iz636/mWW24JwPXXXw/Ayiuv3J+HVEclO9VUU7HjjjsCsOyyywJdR2eonLQmRPXz4osvArDYYov1+3jLuJeg/9qIm+mmmw6o+TcBfv3rXwPZ4lHtGcWisnUOnXOV/ejRo+t+9csuuwxoniLfYYcdgGwdyAorrNCUv99o/O5/97vfAXDCCSeM83p5v8h1ZySSewc//vGPgZrFC7W6M+uuu24jhz4eociDIAjanAGnyPUjDhlSy01yp1n/saj6vPuqqFVCZdWnctV37ucaMdGfPPnkk0DNfwxwxx13AFnNOebyMc0666zjvN+oFdXeCy+8UFfpqtc777wTyN+Xav5vf/sb0DhFrvXw9NNP17/brvyKjv83v/nNOM9FX+55550HwP/93//1/4C7wAiGW2+9FchzpJor+5K7svxcVyuttBIwrgr2M1R+jcYxLrnkkgBcffXVQD4H9Bn3B1q6+p2nn75WY8+10IzuP1rlzmVXOCbPIcd8zTXXADXfOOT12EwG1IX8xRdfZN999wXGTSeGvCFmKJuJGZrzYiiSm6LeGNzQ8AT75z//CWRXRH9gwsXhhx8O5Iuq5vrmm28O5Au8myuOadiwYUA+1jKGjwHMNNNMQHYB6Iq6++67gXyhbzRf+tKXuj1JyzfRcsKQJ1izXA6d4Zi6Ohbn0s1zwz8VFIa+ld0Yk002WT28zU3HZqGo8Zgc69133113u/QWN3lNoNLV5Lp0bk2Yuummm4BqJbU5Fje03ZT2fG0m4VoJgiBocwaEIvdOuNVWW9WVpK4SVewRRxwBZPdDGRWAoXz+nu4JzSbdGf1ZLMjxO0Y3tzyGP/zhD0AtFA2yErj44ouB/jE/NaPvu69WcUEVWAW0fvyeyserm+b73/9+C0bXMzbeeGMgK27LEngs5cQ0E9K23XZbttpqK6B707+/0cL5f//v/43z91966aVef6bnlUlrrmVdUm7M+9xEPi2aVpcpGBvdqwYd6I6ceuqpWW655YC+N7ruKaHIgyAI2py2VuRuSO60005AbZNQFevGlxsz3fnWVEaqDjcBDflSEeiPVcH2BxbwKoem+eimimP0WH71q18BcOyxxwKw+uqrAzn5afnllwd6pgrcC/Bv2FS3CpjCX05ici423XRToOu9gSrw+9//HoDTTjsNgCeeeALI60qL0HBWrbDVVlut6Urc9bXooosC+Xs2dLc3obZXXHEFkNeZ1pRrvPw9lH+uxdKZIi+HEvc3WoJafO6XGeigleU4Tj/99PqadRN88cUXb8jYJBR5EARBm9OWity784EHHgjAlVdeCdTuyEahGMbV1V3aokaqPN9nGq4RIfrsvNv6fu+0P/jBD3p9HH6W4XKqkrIKsfWZSsiImnKIpGFiPqpqysc4Nu+8U6tnVo4IMf2/lbhXYCRR2TfuPsUvf/nL5g9uIhk0aBCQo6kMUSsrUK0y53DZZZeth+Q1G/eHXJcyMXsRjzzyCJD3d7SmdtllFyBbk34P7odss802QFbDnhOd0Vslrp/eksOW39AC8hqgxewYHYtz6Nx5vqaU6klDhu+q4htlXYUiD4IgaHPaUpEbg/uXv/wFyIpz5plnrrcLK9/5vJsat6rPSpVXjgX1sSuM7uiLf27bbbcd5285Zh/1+apitCJUACp048MtzHTdddcBubCXO/8zzzzzeGP4xz/+Mc5x+NjK6ADnc5999gFyMSPxuEx8mpBaqwoe0/333w903TyjXEDs/fffr+cXuFfSLMpWqOeK59+E0NJbaqmlgGxpGAljqYvyeWP6v6UPtAYaUaRrtdVWA3LpB/eS/NvG0RsVpbX/85//HMjF3VTmnp/nnntufc787rR4G9X0JBR5EARBm9OWilzcRfdu/95779VVmndJs6y8Q5ohVo5jVYGa+fnYY491+jdVy76vLzgGFaWKwBhiX7fBgoracq3uA6jMVUwqWH2cnamZsvLzue9tdAadY/3www/r2Y6WcnUMFjorZxbeddddQPMVal9wH8TY43JWoN+H/WvHLv6mYrT5SiMKtHVGuXSC6tI2dJ3huWgDF/3GnjfmPnS1vizWpgrWx9yI9eg1oRzdZS7JzjvvDOTcE8/H8ljKBdOOOuqoesllo97cewtFHgRBEHRKWypyd/HLha7+97//1X1vFlgqN5BQXXh3tdaI71clu2vu3VdVuPTSSwO5AUBf+O1vfwvkCBFLnZopphLoacamSlYfck9YeOGFx3leLnPbX6j4tXROPfVUoKbAbLKg4vN4VTqqOedik002AbJ1Zd2OKqIyM37czGCPzYgJ16UNPYyK+vTTT+v+VeubXHrppc0Yen1M5ezL22+/fTylLc6hhd/KeK66tv1s14UqWGtSBdsIRW7D60YUudp1110BuPzyy4HO96f6k1DkQRAEbU5bKnLvzvoMf/jDHwI1pW41QpWQ/jzv8EaKGP+tX0slbnF5Gx+oDq3KZtakO9h9UQr6wo0xthxmM6IwHHe59kyjfONGzujzVJl+9tlndaWnatcqMGKhHDVh6VM/6/TTTwdg1VVXbegxTAyO9eyzzwZyHLHrySqT5WxU48f3339/oObHLUe8NAstPC0BLcVPP/20XoFTf7KZzlrJ+v61OFTwxv6bFelaLzerdk00UpE3knKklVUcG0Uo8iAIgjanLRW5lLPlUkpd3rlVd2YLqhDOOOMMIFc3tHqin2MVQCsTWty/P+Ja9ZM2o3h+V5RVXqN8eVb70/LxmEePHj3enJUjaLpqlO1cGdWj1aVCd66a3QgX4JZbbgFyqzfXm/5865h0xdprrw3UslpVq1o1ja4tUuaYY44BsrXw2muvcckllwC5Do7x4bZNK68rx+p5WG6KomUy55xzArDXXnsBOduynSiKol4HyeuHsemNIhR5EARBm9PWilwmpGRVL1YoswqdisFaECoFfXfuaKvEjW5pRIaZKvVHP/oRkCvENVJJulNv3K6KyfoS/U25znRnLc+MgFhooYUA6t2e9C8a2WD2rhEQUq5N4/tUd77eyBrR+rO32247IB+vWYC33XZbjz7HdmLf+MY36vs1+ou1ZhqxFjtDv7fnzlZbbVXPRvY7d82WMznLlSrLXZ2MF1f127S4SvXwJ5atttqqvjdnhFGjradQ5EEQBG3OgFDkqukzzzyzrlr0uRlXbn1xs0HNElTdqDrcVd9xxx0BGDx4MNBY9WP9F60D1duJJ54I9L1X49g1M/SzqlZVex6//ub+RuVWbqr7la98pa7Ajbk1c7WMVpJzo5/WmGv7jlp5TkvH71GV19d+kxPCLjGuM6OdrGnTVUSSczRixAggV/Z84okn6hZnK3z9Y7POOusANb+49XycM4/XaI1y7Xxxbg866CAgR5FVqfPPxKIVZheoW265pV4h0qzRRhOKPAiCoM1pa0XuXd9IgHfffbd+ZzeCQXWmz051o1LQF+kddMMNNwSy+mhkdxZVmN14jHQwBv7www8H8nF6dy+rurHrIEM+VqN5jEl+++236+99/vnngRyl4mfPPffc/XR042IPQ+PWrdi455571jNbe2r1eJzWuPbRudMKM97ceP0llliiT8cwIfzOVZh+z/7tro7NKoH77bcfkHMJrDD4pS99qd41yBrezfKNl/F7X3/99euZzfq2raHi+rF+jhaeceX6xKsUF65V6jWjp2Nzjp3zYcOGAbXvoNwToNGEIg+CIGhzUmfRA82mo6OjMEtzYtAHqrpT3UC+q5Z7caoMjEHfeuutx3lsRW1rFbjK1Noaqld9w/oXVXn+3j333APkyBqV+KOPPgrk+OOZZpqp7rM1E09fucq8UQpC9eI+wGKLLQY01uJx7o3DbmS9C4/P9eV55R6L0U9zzDEHkKM8TjrpJCDPmZ9jvZwVV1yxbpn5nbUi32AgYhVN6+CYGaw1bta30VHOkfkLWh3ui8j5559fP8/6QkdHB8OHD++RedCnFZFS2i+l9HhK6bGU0oUppSlTSoNSSvellJ5NKV2cUqp+1f8gCII2pteKPKU0J/B3YKGiKD5JKV0CXA+sBfy1KIqLUkqnAw8XRXHahD6rt4rc7DCzyh566KG6CjPO1Z1+M+mMdFAZtToSALJ6K2eVWp/DMRqbqgL35/qCjW5RDRiBY72LjTfeuK7qVRtB/+AcutdQzk8ox37rU9dn7HMja37yk58AtQidZtUfn1RwLqwRbscf9yVU3maBO7f60j13Vl99dQCOPPJIIO/V9dceRtMUObXN0qlSSpMDUwOvAysDl435+bnABn38G0EQBMEE6JOPPKW0D3AM8AlwM7APcG9RFPON+fncwA1FUUywYEJvFblj11f+zjvv1LvGmLloVEq5tnI74vEaiWM3JF9Xgetb99hbFeUwKfLSSy8BOQrFTM5yn0vXob5wMyJ/8YtfANm3bqZx0DiMA3ePTZ+31xX3PYyI02oy47hRc9QURZ5Smh5YHxgEzAFMA6zZyVs7vVOklHZNKQ1PKQ23vVUQBEEw8fTFR74psEZRFDuNeb4tsCywKTBbURSjUkrLAr8simL1CX1WbxV5EATBQKVZPvKXgWVSSlOnmp24CvAEcBuwyZj3bAdc1Ye/EQRBEHRDry/kRVHcR21T8wHg0TGfNRQ4GNg/pfQcMCNwVj+MMwiCIOiCPsXeFUVxBHBE6eXngaX78rlBEARBz2l9EHUQVIje1t0IglZSiQt5URSMGjWqEsk5Qc9oZXu6RhLJN+2HZQ0aWe6h6gysszAIgmASpBISOKUUarzNGGhKPGhfJmUlLnE2BkEQtDkhg7vBYkZuglnkqJ3wGD7//PN6C7ygNbi3YAmJRjaCDiYdQpEHQRC0OaHIS1iy4MwzzwRy8SObOTz22GMtGdfEoAK3YP4BBxwAwOyzz879998PNLbJQpDnYOjQoeM8WorYn+vfdT5cX+1o+Y2Nx2c5aQtQWarZUrA23o49l74R314QBEGbE4p8DPou//CHPwBw2GGHAbl5hQkiVWbEiBEAbLHFFkAunD/XXHMBcMUVV/RLC6q+oMVjs4snn3ySK6+8EoBrr70WgI8++gjI5XcXX3xxAKaffnoA9t57bwBWW221Jo164rE0qk0HLJGqUjXRyMYTzp3zYzlcG6BUAfeJPv300/o8Wuzu//7v/4Dc/uzNN98E8vnj+1XelljedNNNATjhhBMAYg+nl4QiD4IgaHMmeUVuO6ddd90VgBtvvBHISsJmzPrKq4RqbqWVVgKyAlf17LbbbgAcddRRQDWaFGy00UYAXHXV+EUxyw2zxdZ2+pOfffZZAIYNGwZki6NKOAc2zn744YeBbOltu+22QFa5a6655jjvO+uss8Z5fxXw3Nh+++3rUTeipdFVWexyqQOtrssvvxyAPfbYA8jNGoKJIxR5EARBm9OnVm/9RTMbS7h7fvPNNwNw4YUXAnDXXXcB49dt2HDDDQH43e9+B1SjFocqbuuttwZyyzfbhtmqytZvVcAuULPPPjuQFRx0X5jKn6tytSz0kV988cVANVva3XTTTUCO3thggwm3sB00aBCQfeq29asCWqkzzjhj3RrUYvW582qmthaJDY4936644gogn2//7//9PwB22mmnxh5EL/CYbrjhBgCeeuopIFsT7vfYCm6uueaqr0nbwun7n5gibM1svhwEQRC0mAHvI/duusMOOwBw6aWXAtkPazSKu+i77LILAD/+8Y+BHD9eBbSeVAb6U7UejByokhIX49c7o6y4xefOoXOm+nvwwQeB7G/VIqkSK664IpDVWndoTc0333wAvP322/W12WpUlY888khdhWod6fM3WsfolXINpaWXrrUquOaaa4A8l1Wsl6K1YEPs008/Hchj9rG8p/PGG2/UI628frz11lsALLnkkgBMO+20APzmN78BYO655+7TWEORB0EQtDkDVpHrR15mmWWA7NdS1arE999/f6C2Ew/VjIAQ/cxmbI4cORLIfshNNtmk81+sAPqzVSjGSU8xxRR1dVL2H/q6Mdb6aI2YeP311wF4+eWXAVhkkUUaNfxeow+5pxx00EFAXqc9VfLN5Fvf+tZ4Vt+///1voPsMzddeew3IVpTHWYWIKtEC1F9vfoPWhhbS8ssvD9QsFMjr8u23366v1eeffx7I0XFa035PPv75z38Get/IJBR5EARBmzPgFLmxxXvttReQfXUqo9133x2AQw89FIAZZpgBqHZLL31wxotrXXg3Nya7ysdQjgGXCY3Z47777rsBWGeddQD44IMPgOzD1Fe+4IILAuP7ZVuJirO7uTn++OMBuOWWWwAYPHgwUI0oqZ7QnRJ3roy0cm5Vt+uu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\n",
      "text/plain": [
       "<Figure size 432x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Testing\n",
    "# Generate images from noise, using the generator network.\n",
    "n = 6\n",
    "canvas = np.empty((28 * n, 28 * n))\n",
    "for i in range(n):\n",
    "    # Noise input.\n",
    "    z = np.random.normal(-1., 1., size=[n, noise_dim]).astype(np.float32)\n",
    "    # Generate image from noise.\n",
    "    g = generator(z).numpy()\n",
    "    # Rescale to original [0, 1]\n",
    "    g = (g + 1.) / 2\n",
    "    # Reverse colours for better display\n",
    "    g = -1 * (g - 1)\n",
    "    for j in range(n):\n",
    "        # Draw the generated digits\n",
    "        canvas[i * 28:(i + 1) * 28, j * 28:(j + 1) * 28] = g[j].reshape([28, 28])\n",
    "\n",
    "plt.figure(figsize=(n, n))\n",
    "plt.imshow(canvas, origin=\"upper\", cmap=\"gray\")\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
